/*
 * Copyright (c) 2015, the DeltaSync# project. All Rights Reserved.
 * This project is a C# port of the JDeltaSync project:
 *     http://jdeltasync.googlecode.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;

namespace PM.Net.DeltaSync {
    /**
     * Abstract {@link Store} implementation.
     */
    public abstract class AbstractStore : Store {
        [Serializable]
        public class State {
            public string foldersSyncKey = "0";
            public Dictionary<string, string> messagesSyncKeys = new Dictionary<string, string>();
            public Dictionary<string, Folder> folders          = new Dictionary<string, Folder>();
            public Dictionary<string, Dictionary<string, Message>> 
                                              messages         = new Dictionary<string, Dictionary<string, Message>>();
        }
    
        protected abstract State GetState(string username);
        
        protected virtual void StateChanged(string username, State state) {}
    
        protected static T AddElement<K, T>(IDictionary<K, T> collection, K key, T element) {
            if (collection.ContainsKey(key))
                collection[key] = element;
            else    
                collection.Add(key, element);
            return element;
        }
        
        private Dictionary<string, Message> GetMessagesMap(State state, Folder folder) {
            Dictionary<string, Message> map;
            if (state.messages.ContainsKey(folder.Id))
                map = state.messages[folder.Id];
            else {
                map = new Dictionary<string, Message>();
                state.messages.Add(folder.Id, map);
            }
            return map;
        }
        
        public string GetFoldersSyncKey(string username) {
            return GetState(username).foldersSyncKey;
        }
        
        public string GetMessagesSyncKey(string username, Folder folder) {
            var    keys = GetState(username).messagesSyncKeys;
            string syncKey;
            if (keys.ContainsKey(folder.Id)) 
                syncKey = keys[folder.Id];
            else {
                syncKey = "0";
                keys.Add(folder.Id, syncKey);
            }
            return syncKey;
        }
        
        public void UpdateFolders(string username, string syncKey,
                IEnumerable<Folder> added, 
                IEnumerable<string> deleted) {
            State state = GetState(username);
            state.foldersSyncKey = syncKey;
            foreach (Folder folder in added)
                AddElement<string, Folder>(state.folders, folder.Id, folder);
            foreach (string id in deleted)
                state.folders.Remove(id);
            StateChanged(username, state);
        }
        
        public void ResetFolders(string username) {
            State state = GetState(username);
            state.foldersSyncKey = "0";
            state.folders.Clear();
            StateChanged(username, state);
        }
        
        public void UpdateMessages(string username, Folder folder, string syncKey,
                IEnumerable<Message> added,
                IEnumerable<string>  deleted) {
            State state = GetState(username);
            AddElement<string, string>(state.messagesSyncKeys, folder.Id, syncKey);
            Dictionary<string, Message> map = GetMessagesMap(state, folder);
            int count = 0;
            foreach (Message message in added) {
                AddElement<string, Message>(map, message.Id, message);
                count++;
            }
            foreach (string id in deleted) {
                map.Remove(id);
                count++;
            }
            if (count > 0)
                StateChanged(username, state);
        }
        
        public void ResetMessages(string username, Folder folder) {
            State state = GetState(username);
            AddElement<string, string>(state.messagesSyncKeys, folder.Id, "0");
            GetMessagesMap(state, folder).Clear();
            StateChanged(username, state);
        }
        
        public List<Folder> GetFolders(string username) {
            return new List<Folder>(GetState(username).folders.Values);
        }
        
        public List<Message> GetMessages(string username, Folder folder) {
            return new List<Message>(GetMessagesMap(GetState(username), folder).Values);
        }
    }
}